﻿
using F29ServerProcess.Interfaces;
using IotContract.Dtos.Clients;
using IotContract.Dtos.Remotes.Request;
using IotContract.Dtos.Servers.Process;
using IotContract.Dtos.Servers.Process.F29;
using IotContract.Dtos.Servers.Pushs;
using IotContract.Enums;
using IotContract.Interfaces;
using IotContract.Interfaces.Process;
using IotContract.Models.Clients;
using IotContract.Models.Clients.Hks;

using IotContract.Models.Jobs.F29;
using IotContract.Models.Process;
using IotContract.Models.Process.F29;
using IotContract.Models.Writes.F29;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XT.Common.Extensions;
using XT.Common.Interfaces;
using XT.Common.Models.Server;


namespace F29ServerProcess.Services;
public class ProcessDataService : IProcessDataService
{
    /// <summary>
    /// 数据处理中心
    /// </summary>
    private readonly IServerMainManager _serverMainManager;
    /// <summary>
    /// 用来推送错误报警信息
    /// </summary>
    private readonly ISignalRService _signalRService;
    /// <summary>
    /// api通用服务
    /// </summary>
    private readonly IProcessApiHandle _processApiHandle;

    /// <summary>
    /// 入库站台组,头站台 叉1位置
    /// </summary>
    private ConcurrentDictionary<int, EnterTagGroupDto> _enterTag1Dic = new ConcurrentDictionary<int, EnterTagGroupDto>();
    /// <summary>
    /// 入库站台组,尾站台 叉2位置
    /// </summary>
    private ConcurrentDictionary<int, EnterTagGroupDto> _enterTag2Dic = new ConcurrentDictionary<int, EnterTagGroupDto>();

    private ConcurrentBag<int> _firstEnterTags = new ConcurrentBag<int>();

    public ProcessDataService(IServerMainManager serverMainManager,ISignalRService signalRService,IProcessApiHandle processApiHandle) 
    {
        _serverMainManager = serverMainManager;
        _processApiHandle = processApiHandle;
        _signalRService = signalRService;
        // url路径
    }


    /// <summary>
    /// 扫码枪数据
    /// </summary>
    public ConcurrentDictionary<string, List<PushBcrDto>> Bcrs { get; set; } = new ConcurrentDictionary<string, List<PushBcrDto>>();

    /// <summary>
    /// 物流线数据
    /// </summary>
    public ConcurrentDictionary<int, F29CvUnitDto> CvUnitTags { get; set; } = new ConcurrentDictionary<int, F29CvUnitDto>();

    /// <summary>
    /// 堆垛机数据
    /// </summary>
    public ConcurrentDictionary<string, F29ScUnitDto> ScUnits { get; set; } = new ConcurrentDictionary<string, F29ScUnitDto>();

    /// <summary>
    /// 充放电数据
    /// </summary>
    public ConcurrentDictionary<string, F29ProcessFmtData> Fmts { get; set; } = new ConcurrentDictionary<string, F29ProcessFmtData>();
    /// <summary>
    /// 拘束机数据
    /// </summary>
    public ConcurrentDictionary<string, F29RestraintUnitDto> RestraintUnits { get; set; } = new ConcurrentDictionary<string, F29RestraintUnitDto>();

    /// <summary>
    /// 换盘机数据
    /// </summary>
    public ConcurrentDictionary<string, F29CebUnitDto> CebUnits { get; set; } = new ConcurrentDictionary<string, F29CebUnitDto>();

    /// <summary>
    /// 插钉机数据
    /// </summary>
    public ConcurrentDictionary<string, F29PinInUnitDto> PinInUnits { get; set; } = new ConcurrentDictionary<string, F29PinInUnitDto>();

    /// <summary>
    /// 拔钉机数据
    /// </summary>
    public ConcurrentDictionary<string, F29PinOutUnitDto> PinOutUnits { get; set; } = new ConcurrentDictionary<string, F29PinOutUnitDto>();

    /// <summary>
    /// 托盘Job
    /// </summary>
    private ConcurrentDictionary<int, List<string>> TaskTrayDic { get; set; } = new ConcurrentDictionary<int, List<string>>();


    #region 数据写入

    /// <summary>
    /// 执行相关业务
    /// </summary>
    /// <param name="job"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public async Task ExecuteData(ProcessBaseData job, ProcessProviderType type)
    {

        if (type == ProcessProviderType.Cv)
        {
            // 清理后重新扫码
           var tag= CvUnitTags.GetValueOrDefault(job.Num.To<int>());
            if (tag != null)
            {
                ClearTag(tag);
            }
        }
    }

    #region 写入物流线
    /// <summary>
    /// 获取另外的入库站台
    /// </summary>
    /// <param name="tagNum"></param>
    /// <returns></returns>
    public F29CvUnitDto? GetEnterOtherTag(int tagNum)
    {
        if (_enterTag1Dic.ContainsKey(tagNum))
        {
            var group= _enterTag1Dic.GetValueOrDefault(tagNum);
            if(group != null)
            {
                return CvUnitTags.GetValueOrDefault(group.TagNumber2.To<int>());
            }
        }
        else if(_enterTag2Dic.ContainsKey(tagNum))
        {
            var group = _enterTag2Dic.GetValueOrDefault(tagNum);
            if (group != null)
            {
                return CvUnitTags.GetValueOrDefault(group.TagNumber1.To<int>());
            }
        }
        return null;
    }
    /// <summary>
    /// 缓存维护托盘和任务
    /// </summary>
    /// <param name="unit"></param>
    public void SetTrayByTask(F29CvUnitDto unit)
    {
        if (unit.IsExistTray1 == false)
        {
            unit.Tray1 = unit.Tray2 = string.Empty;
            return;
        }
        // 拥有扫码数据
        if (unit.CommitReturnTask > 0)
        {
            if (unit.Trays.Count > 0)
            {
                if(TaskTrayDic.ContainsKey(unit.CommitReturnTask))
                {
                    TaskTrayDic.TryUpdate(unit.CommitReturnTask, unit.Trays, TaskTrayDic.GetValueOrDefault(unit.CommitReturnTask));
                }
                else
                {
                    TaskTrayDic.TryAdd(unit.CommitReturnTask,unit.Trays);
                }
            }
        }
        else
        {
            if (unit.TaskNumber > 0)
            {
                if (TaskTrayDic.ContainsKey(unit.TaskNumber))
                {
                    var trays = TaskTrayDic.GetValueOrDefault(unit.TaskNumber);
                    unit.Tray1 = trays.FirstOrDefault();
                    if (trays.Count > 1)
                    {
                        unit.Tray2 = trays.LastOrDefault();
                    }
                }
            }
        }
    }

    /// <summary>
    /// 清除tag提交信息
    /// </summary>
    /// <param name="unit"></param>
    public void ClearTag(F29CvUnitDto unit)
    {
        if (unit.Commiting == true) return;
        if (unit.IsExistTray1)
        {

            // 正常行走状态
            if (unit.Message.IsNullOrEmpty() && unit.CommitReturnTask == unit.CommitTask && unit.Dest != unit.TagNum)
            {
                return;
            }

            if (GetUnitTagType(unit.TagNum) > 0)
            {
                unit.Tray1Old = string.Empty;
                unit.Tray2Old = string.Empty;

                // 如果是双叉后站台，清理前站台
                if (GetUnitTagType(unit.TagNum) == 3)
                {
                    var tag1 = GetEnterOtherTag(unit.TagNum);
                    if (tag1 != null)
                    {
                        ClearTag(tag1);
                    }
                }
            }

        }
        else
        {
            unit.Tray1Old = string.Empty;
            unit.Tray2Old = string.Empty;
        }

        unit.CommitReturnTask = 0;
        unit.CommitTask = 0;
        unit.Message = string.Empty;
        unit.Tray1 = string.Empty;
        unit.Tray2 = string.Empty;
        unit.BcrReadID = string.Empty;
    }

    /// <summary>
    /// 获取入库站台类型
    /// </summary>
    /// <param name="tagNum"></param>
    /// <returns>0非入库站台  1单叉站台  2双叉前站台  3双叉后站台</returns>
    public int GetUnitTagType(int tagNum)
    {
        if (_enterTag1Dic.ContainsKey(tagNum))
        {
            return 2;
        }
        if (_enterTag2Dic.ContainsKey(tagNum))
        {
            return 3;
        }
        if (_firstEnterTags.Contains(tagNum))
        {
            return 1;
        }

        return 0;
    }

    /// <summary>
    /// 写入物流线
    /// </summary>
    /// <param name="trayResponse"></param>
    /// <returns></returns>
    public async Task<bool> WriteCv(CvTrayTask trayResponse)
    {
       var cv= CvUnitTags.GetValueOrDefault(trayResponse.TagNumber);
        if (cv == null) return false;
        int status = trayResponse.TrayType == 1 ? 2 : 1;
        string props = "W_TASK,W_DEST,W_DONE";
        string values = $"{trayResponse.TaskNum},{trayResponse.Dest},{status}";

        var commands = ChangeToCommands(props, values, cv.Tags);

        var result= await _serverMainManager.AddWrites(commands);

        // 和拘束机进行联动,扫码站台
        var restrain = RestraintUnits.Values.FirstOrDefault(x => x.BindNum == trayResponse.TagNumber.ToString());
        // 扫码发向拘束机
        if (restrain!=null && restrain.UnitNum==trayResponse.Dest.ToString())
        {
            // 向拘束机写入托盘号
            await WriteRestraint("W_TRAY", restrain.EquipNum, cv.Tray1);
        }
        return result;
    }

    /// <summary>
    /// 写入物流线状态
    /// </summary>
    /// <param name="trayResponse"></param>
    /// <returns></returns>
    public async Task<bool> WriteCvStatus(int tagNumber, int value)
    {
        var cv = CvUnitTags.GetValueOrDefault(tagNumber);
        if (cv == null) return false;

        string props = "W_DONE";
        string values = $"{value}";

        var commands = ChangeToCommands(props, values, cv.Tags);

        return await _serverMainManager.AddWrites(commands);
    }
    #endregion

    #region 写入堆垛机
    /// <summary>
    /// 写入堆垛机-启动
    /// </summary>
    /// <param name="write"></param>
    /// <param name="equipNum"></param>
    /// <returns></returns>
    public async Task<bool> WriteSCToRun(F29ScPlcWrite write, string equipNum)
    {
        var sc = ScUnits.GetValueOrDefault(equipNum);
        if (sc == null) return false;


        string props = string.Empty;
        string values = string.Empty;



        if (write.UsePoke == 1)
        {
            props = "W_FROM_X,W_FROM_Y,W_FROM_Z,W_TO_X,W_TO_Y,W_TO_Z,W_TRAY";

            values = $"{write.FromX},{write.FromY},{write.FromZ},{write.ToX},{write.ToY},{write.ToZ},{write.Tray}";



        }
        else if (write.UsePoke == 2)
        {
            props = "W_POKE_USE,W_FROM_X2,W_FROM_Y2,W_FROM_Z2,W_TO_X2,W_TO_Y2,W_TO_Z2,W_RIGHT_TRAY";

            values = $"{write.UsePoke},{write.FromX2},{write.FromY2},{write.FromZ2},{write.ToX2},{write.ToY2},{write.ToZ2},{write.RightTray}";
        }
        else if (write.UsePoke == 3 || write.UsePoke == 4)
        {
            props = "W_POKE_USE,W_FROM_X,W_FROM_Y,W_FROM_Z,W_TO_X,W_TO_Y,W_TO_Z,W_TRAY,W_FROM_X2,W_FROM_Y2,W_FROM_Z2,W_TO_X2,W_TO_Y2,W_TO_Z2,W_RIGHT_TRAY";

            values = $"{write.UsePoke},{write.FromX},{write.FromY},{write.FromZ},{write.ToX},{write.ToY},{write.ToZ},{write.Tray},{write.FromX2},{write.FromY2},{write.FromZ2},{write.ToX2},{write.ToY2},{write.ToZ2},{write.RightTray}";
        }



        if (write.Tray2.IsNotNullOrEmpty())
        {
            props += ",W_TRAY2";
            values += $",{write.Tray2}";
        }
        if (write.RightTray2.IsNotNullOrEmpty())
        {
            props += ",W_RIGHT_TRAY2";
            values += $",{write.RightTray2}";
        }
        props += ",W_POKE_USE,W_SEND";
        values += $",{write.UsePoke},1";


        var commands = ChangeToCommands(props, values, sc.Tags);

        return await _serverMainManager.AddWrites(commands);
    }
    /// <summary>
    /// 写入堆垛机-完成
    /// </summary>
    /// <param name="equipNum"></param>
    /// <returns></returns>
    public async Task<bool> WriteSCToComplete(string equipNum)
    {
        var sc = ScUnits.GetValueOrDefault(equipNum);
        if (sc == null) return false;
        string props = "W_SEND_END";
        string values = "1";

        var commands = ChangeToCommands(props, values, sc.Tags);

        return await _serverMainManager.AddWrites(commands);


    }
    /// <summary>
    /// 转化为堆垛机写入命令
    /// </summary>
    /// <param name="prop"></param>
    /// <param name="value"></param>
    /// <param name="tags"></param>
    /// <returns></returns>
    private List<OperationWriteCommand> ChangeToCommands(string prop, string value, List<PushEquipTag> tags)
    {
        var props = prop.Split(',');
        var values = value.Split(",");
        var commands = new List<OperationWriteCommand>();
     
       
        for (int i = 0; i < props.Length; i++)
        {
            var tag = tags.FirstOrDefault(x => x.TagProperty == props[i]);
            if (tag == null) continue;
            commands.Add(new OperationWriteCommand
            {
                EquipId = tag.EquipInfoId,
                TagId = tag.Id,
                Value = values[i],
                ProtobufType = IotProtobufType.SIEMENS_S7.ToString()
            });
        }
        return commands;
    }
    #endregion

    #region 写入换盘机
    /// <summary>
    /// 写入换盘机
    /// </summary>
    /// <param name="tagProperty"></param>
    /// <param name="value"></param>
    /// <param name="equipNum"></param>
    /// <returns></returns>
    public async Task<bool> WriteCeb(string tagProperty, string value, string equipNum)
    {
        var restraint = CebUnits.GetValueOrDefault(equipNum);
        if (restraint == null) return false;


        string props = tagProperty;
        string values = value;
        var commands = ChangeToCommands(props, values, restraint.Tags);

        return await _serverMainManager.AddWrites(commands);
    }
    #endregion

    #region 写入拘束机
    public async Task<bool> WriteRestraint(string tagProperty,string equipNum,string value="1")
    {
        var restraint = RestraintUnits.GetValueOrDefault(equipNum);
        if (restraint == null) return false;


        string props = tagProperty;
        string values =value;
        var commands = ChangeToCommands(props, values, restraint.Tags);

        return await _serverMainManager.AddWrites(commands);

    }
    #endregion

    #region 写插钉机
    public async Task<bool> WritePinIn(string tagProperty, string equipNum)
    {
        var pinIn = PinInUnits.GetValueOrDefault(equipNum);
        if (pinIn == null) return false;
        string props = tagProperty;
        string values = "1";
        var commands = ChangeToCommands(props, values, pinIn.Tags);
        return await _serverMainManager.AddWrites(commands);
    }
    #endregion



    #region 写拔钉机
    public async Task<bool> WritePinOut(string tagProperty, string equipNum)
    {
        var pinOut = PinOutUnits.GetValueOrDefault(equipNum);
        if (pinOut == null) return false;
        string props = tagProperty;
        string values = "1";
        var commands = ChangeToCommands(props, values, pinOut.Tags);
        return await _serverMainManager.AddWrites(commands);
    }
    #endregion

    #region 写入扫码枪
    /// <summary>
    /// 写入扫码枪
    /// </summary>
    /// <param name="bcrId"></param>
    /// <param name="equipId"></param>
    /// <returns></returns>
    public async Task<bool> WriteBcr(string bcrId,long equipId)
    {
      return await _serverMainManager.AddWrite(new OperationWriteCommand
        {
            Id = bcrId,
            EquipId = equipId,
            ProtobufType = IotProtobufType.HBC_BCR.ToString()
        });
    }
    #endregion

    /// <summary>
    /// 添加报警信息,该报警信息直接通过signalR推送
    /// </summary>
    /// <param name="warns"></param>
    /// <returns></returns>
    public async Task<bool> AddWarns(IEnumerable<IotEquipWarnDto> warns)
    {
        try
        {
            foreach(var warn in warns)
            {
               await _signalRService.WriteMessage(new XT.Common.Models.SignalR.InformModel
                {
                    Message = warn.ToJson(),
                    Type = XT.Common.Models.SignalR.InformTypeEnum.Stop,
                    ContractTag = 110,
                });
            }
            return true;
        }
        catch(Exception)
        {
            return false;
        }
    }


    #endregion

    #region 远程接口
    /// <summary>
    /// 任务完成
    /// </summary>
    /// <param name="jobId"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<string>> CompleteJob(string jobId, string jobId2)
    {
       return await _processApiHandle.CompleteJob(jobId, jobId2);
    }
    /// <summary>
    /// 验证换盘进入
    /// </summary>
    /// <param name="fmsTray"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<string>> ValidateCebEnterTray(FmsTrayRequest fmsTray)
    {
        return await _processApiHandle.ValidateCebEnterTray(fmsTray);
    }
    /// <summary>
    /// 获取通道
    /// </summary>
    /// <param name="fmsTray"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<List<FMTCell>>> GetCebChannel(FmsTrayRequest fmsTray)
    {
        return await _processApiHandle.GetCebChannel(fmsTray);
    }
    /// <summary>
    /// 换盘结束通知
    /// </summary>
    /// <param name="fmsTray"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<string>> CebWorkDoneInform(FmsTrayRequest fmsTray)
    {
        return await _processApiHandle.CebWorkDoneInform(fmsTray);
    }
    /// <summary>
    /// 验证换盘出去
    /// </summary>
    /// <param name="fmsTray"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<string>> ValidateCebOutTray(FmsTrayRequest fmsTray)
    {
        return await _processApiHandle.ValidateCebOutTray(fmsTray);
    }
    /// <summary>
    /// 验证托盘进入
    /// </summary>
    /// <param name="equipNum"></param>
    /// <param name="equipType"></param>
    /// <param name="tray"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<string>> ValidateEnterTray(string equipNum, IotEquipType equipType, string tray)
    {
        return await _processApiHandle.ValidateEnterTray(equipNum,equipType,tray);
    }
    /// <summary>
    /// 验证托盘出去
    /// </summary>
    /// <param name="equipNum"></param>
    /// <param name="equipType"></param>
    /// <param name="tray"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<string>> ValidateOutTray(string equipNum, IotEquipType equipType, string tray)
    {
        return await _processApiHandle.ValidateOutTray(equipNum, equipType, tray);
    }
   
    /// <summary>
    /// 提交叠盘完成
    /// </summary>
    /// <param name="tagNumber"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<string>> CommitStackOver(int tagNumber)
    {
      
        return await _processApiHandle.CommitStackOver(tagNumber);
    }
    /// <summary>
    /// 扫码后提交托盘
    /// </summary>
    /// <param name="unit"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<CvTrayTask>> CommitTrays(F29CvUnitDto unit)
    {
     
        return await _processApiHandle.CommitTrays(new FmsCommitTrayRequest
        {
            TagNumber=unit.TagNum,
            TaskNo=unit.TaskNumber,
            Trays=unit.Trays
        });
    }
    /// <summary>
    /// 扫码后入库站台提交
    /// </summary>
    /// <param name="unit2"></param>
    /// <param name="unit1"></param>
    /// <returns></returns>
    public async Task<AdminCodeResult<F29EnterJob>> CommitEnterTrays(F29CvUnitDto unit2,F29CvUnitDto unit1)
    {
        return await _processApiHandle.CommitEnterTrays(new FmsCommitTrayRequest
        {
            TagNumber=unit1.TagNum,
            TagNumber2=unit2.TagNum,
            TaskNo=unit1.TaskNumber,
            TaskNo2=unit2.TaskNumber,
            Trays=unit1.Trays,
            Trays2=unit2.Trays
        });
    }
    #endregion


    #region 初始化数据
    /// <summary>
    /// 初始化数据
    /// </summary>
    /// <returns></returns>
    public async Task IniData()
    {
       var groups= await  _processApiHandle.GetEnterTags();

        foreach (var group in groups.Result)
        {
            int tag1 = group.TagNumber1.To<int>();
            int tag2 = group.TagNumber2.To<int>();

            if (tag2 != 0)
            {
                _enterTag1Dic.TryAdd(tag1, group);
                _enterTag2Dic.TryAdd(tag2, group);
            }
            else
            _firstEnterTags.Add(tag1);
        }
    }
    #endregion


    #region 获取数据
    /// <summary>
    /// 获取所有数据
    /// </summary>
    /// <returns></returns>
    public List<F29ProcessData> GetAllEquips()
    {
        List<F29ProcessData> datas = new List<F29ProcessData>();
        // 拘束机
        RestraintUnits.Values.ForEach(x =>
        {
            if (x.Connection == false) return;
            datas.Add(new F29ProcessData
            {
                Num = x.EquipNum,
                Connection = x.Connection,
                Error = x.Error,
                Mode=x.Mode.ToString(),
                Message = x.Message,
                Obj = x.Tray
            });
        });
        CebUnits.Values.ForEach(x =>
        {
            if (x.Connection == false) return;
            datas.Add(new F29ProcessData
            {
                Num = x.EquipNum,
                Connection = x.Connection,
                Mode = x.A_Mode.ToString(),
                Message = x.Message,
                Obj = $"tray1 {x.A_Tray1} tray3 {x.A_Tray3} cell1 {x.CellInfo1}, Tray2 {x.A_Tray2} tray4 {x.A_Tray4} cell2 {x.CellInfo2} "
            });
        });

        PinInUnits.Values.ForEach(x =>
        {
            if (x.Connection == false) return;
            datas.Add(new F29ProcessData
            {
                Num = x.EquipNum,
                Connection = x.Connection,
                Error = x.Error,
                Mode = x.Mode.ToString(),
                Message = x.Message,
                Obj = x.Tray
            });

        });
        PinOutUnits.Values.ForEach(x =>
        {
            if (x.Connection == false) return;
            datas.Add(new F29ProcessData
            {
                Num = x.EquipNum,
                Connection = x.Connection,
                Error = x.Error,
                Mode = x.Mode.ToString(),
                Message = x.Message,
                Obj = x.Tray
            });
        });

       var mnets= _serverMainManager.GetEquips<LgPushNetDto>(new ClientRequestCommand
        {
            ProtobufType=IotProtobufType.MNET_29.ToString()
        }).Result;

        mnets.ForEach(x =>
        {
            if (x.Connection == false) return;
            datas.Add(new F29ProcessData
            {
                Num = x.Num,
                Connection = x.Connection,
               
                Mode = x.EquipStatus,
                Message = x.ExMessage,
                Obj = x.Obj
            });
        });

        return datas;
    }
    #endregion
}
